package com.xcity.logupload.task.ftp;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;

import com.xcity.logupload.Resources;
import com.xcity.logupload.config.CompressMode;
import com.xcity.logupload.config.FtpConfig;
import com.xcity.logupload.task.UploadTask;
import com.xcity.logupload.util.IOUtils;

public class FtpUploadTask extends UploadTask {

	private static final Logger LOG = Resources.LOG;
	
	@Override
	public boolean upload() throws Exception {
		FTPClient ftp = new FTPClient();
		try {
			upload0(ftp);
		} catch (Exception e) {
			throw e;
		} finally {
			ftp.disconnect();
		}
		
		
		return true;
	}
	
	private boolean upload0(FTPClient ftp) throws Exception {
		FtpConfig config = Resources.getFtpConfig();
		
		FTPClientConfig ftpConfig = new FTPClientConfig();
		ftp.configure(ftpConfig);
		ftp.connect(config.host, config.port);
		int code = ftp.getReplyCode();
		if (!FTPReply.isPositiveCompletion(code)) {
			LOG.error("connect ftp server failed: host={}, port={}, replycode={}", config.host, config.port, code);
			return false;
		}
		if (!ftp.login(config.user, config.password)) {
			LOG.error("ftp login failed: host={}, port={}, user={}, password={}", config.host, config.port, config.user, config.password);
			return false;
		}
		
		ftp.enterLocalPassiveMode();
		ftp.setFileType(FTP.BINARY_FILE_TYPE);
		ftp.setFileTransferMode(FTP.COMPRESSED_TRANSFER_MODE);
		
		String localFileName = getLocalFileName();
		String remoteFileName = getRemoteFileName();
		
		Path tmp = Paths.get("tmp");
		IOUtils.createDir(tmp);
		System.out.println(tmp.toAbsolutePath());
		
		DirectoryStream<Path> localServerDirs = Files.newDirectoryStream(Paths.get(config.localWorkDir));
		
		for (Path localServerDir : localServerDirs) {
			if (Files.isDirectory(localServerDir)
					&& localServerDir.getFileName().toString().startsWith(config.serverPrefix)) {
				// 1. 复制日志文件到临时目录
				// eg. /data/game-server-v3/city-server1/logs/server.log.2017-07-04 -> /data/game-server-v3/tools/logs_upload/tmp/server.log.2017-07-04
				// 2. 压缩日志
				// eg. /data/game-server-v3/tools/logs_upload/tmp/server.log.2017-07-04 -> /data/game-server-v3/tools/logs_upload/tmp/server.log.2017-07-04.zip
				// 3. 上传压缩后的日志文件
				// eg. /data/game-server-v3/tools/logs_upload/tmp/server.log.2017-07-04.zip -> /game_server_logs/city-server1/server.log.2017-07-04.zip
				long t = System.currentTimeMillis();
				LOG.info("try upload {}:{}", localServerDir.toAbsolutePath(), localFileName);
				IOUtils.batchDelete(tmp, false); // 清空临时目录
				String remoteDir = config.remoteWorkDir + '/' + localServerDir.getFileName().toString(); // /game_server_logs/city-server1
				code = ftp.list(remoteDir);
				if (!FTPReply.isPositiveCompletion(code)) {
					ftp.makeDirectory(remoteDir);
				}
				// 将日志文件复制到临时目录
				Path localFileSrc = Paths.get(localServerDir.toAbsolutePath().toString(), "logs", localFileName);
				Path localFileTmp = Paths.get(tmp.toAbsolutePath().toString(), remoteFileName);
				IOUtils.copy(localFileSrc, localFileTmp);
				// 压缩
				localFileTmp = doCompress(localFileTmp);
				
				String remotePath = remoteDir + '/' + localFileTmp.getFileName().toString();
				InputStream localStream = null;
				try {
					localStream = new BufferedInputStream(new FileInputStream(localFileTmp.toFile()), 1024000); // buffer: 1000k
					ftp.storeFile(remotePath, localStream);
				} finally {
					IOUtils.closeQuietly(localStream);
				}
				t = System.currentTimeMillis() - t;
				LOG.info("uploaded[{}s] {} -> {}", t / 1000, localFileSrc.toAbsolutePath(), remotePath);
			}
		}
		return true;
	}

	@Override
	public String getLocalFileName() {
		return "server.log";
	}

	@Override
	public String getRemoteFileName() {
		StringBuilder builder = new StringBuilder(32);
		DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
		builder.append("server.log.").append(df.format(new Date()));
//		CompressMode cm = Resources.getFtpConfig().compressMode;
//		if (cm == CompressMode.ZIP) {
//			builder.append(".zip");
//		} else if (cm == CompressMode.GZIP) {
//			builder.append(".gz");
//		}
		return builder.toString();
	}
	
	private static Path doCompress(Path source) {
		CompressMode cm = Resources.getFtpConfig().compressMode;
		if (cm == CompressMode.ZIP) {
			Path target = Paths.get(source.getParent().toAbsolutePath().toString(), source.getFileName().toString() + ".zip");
			IOUtils.zip(source.toFile(), target.toFile());
			return target;
		}
		if (cm == CompressMode.GZIP) {
			// not support
		}
		return source;
	}

}
